package simulator;

import java.util.ArrayList;

public class JournalArea {
	
	private ArrayList<Block> blocks; //number of blocks currently stored in journal area; assume to be infinite capacity
	private int writes; //number of writes to the journal area
	private int maxBlocks; //upper bound on number of blocks. If 0, then we can have an infinite number of blocks in cache
	private int checkptLimit; //number of checkpoitns triggered by journal area size limit reached
	
	public FileSystem fs;
	public SystemSim ss;
	public BufferCache bc;
	
	public void setsystem(FileSystem fs, SystemSim ss, BufferCache bc)
	{
		this.fs = fs;
		this.ss = ss;
		this.bc = bc;
	}
	
	public JournalArea()
	{
		blocks = new ArrayList<Block>();
		maxBlocks = 0;
		checkptLimit = 0;
	}
	
	public JournalArea(int maxBlocks)
	{
		blocks = new ArrayList<Block>();
		this.maxBlocks = maxBlocks;
	}
	
	
	/**
	 * Find the index of the block b, if b has already been written to the journal
	 * @param b
	 * @return
	 */
	public Block findBlock(Block b)
	{
		for(int i = 0; i < blocks.size(); i++){
			Block currentB = blocks.get(i);
			if(currentB.getName().equals(b.getName())){
				return currentB;}
		}
		
		return null;
	}
	
	/**
	 * Commit the block b to the journal area
	 * @param b
	 */
	public void commitData(Block b)
	{
		//we only copy the contents of block b into b, we don't add b to 'blocks' directly since it would become an alias
		
		Block current = findBlock(b);
		
		if(current != null)
		{
			current.setData(b.getData()); //don't duplicate a block into the file system
		}
		
		else{
			Block newB = new Block(b.getName(), b.getData());
			blocks.add(newB);
		}
		
		writes++;
	}
	
	public void commitSync(Block b)
	{
		commitData(b);
		
		if(maxBlocks > 0 && blocks.size() >= maxBlocks){
			Checkpoint();//checkpoint has been triggered, since we must now write changes to file system
			checkptLimit++;
		}
		
	}
	
	public void commitAsync(ArrayList<Block> dirtyBlocks)
	{
		while(dirtyBlocks.size() > 0)
		{
			Block b = dirtyBlocks.remove(0);
			commitData(b);
		}
		
		if(maxBlocks > 0 && blocks.size() >= maxBlocks){
			Checkpoint();//checkpoint has been triggered, since we must now write changes to file system
			checkptLimit++;
		}
	}
	
	/**
	 * Checkpoint data that has been successfully committed
	 * currently NOT used
	 */
	public void Checkpoint()
	{
		while(blocks.isEmpty() == false)
		{//iterate through each committed block, and write to file system
			Block b = blocks.remove(0);
			fs.updateFile(b.getName(), b.getData());
		}
	}
	
	public void checkpointAll()
	{
		while(!blocks.isEmpty())
		{
			Block block = blocks.remove(0);
			String bName = block.getName();
			fs.updateFile(bName, block.getData());
			// Checkpoint, store data into file system
			
			//for(int i=0; i<fs.files.size(); i++)
			//{
				//if(fs.files.get(i).getName().equals(block.getName()))
				//{
					//fs.files.get(i).setData(block.getData());
					//break;
				//}
				
			//}
			// Set block attributes in BufferCache to sync status.
			
			/*for(int j=0; j<bc.frames.length; j++)
			{
				if(bc.frames[j].getName().equals(block.getName()))
				{
					bc.frames[j].setIsDirty(false);
					break;
				}
			}*/
		}
	}
	
	public int getWrites()
	{
		return writes;
	}
	
	public String toString()
	{
		String str = "]n";
		for(int i = 0; i < blocks.size(); i++)
		{
			str += "Block name: " + blocks.get(i).getName()
					+ "\tBlock data: " + blocks.get(i).getData();
		}
		return str;
	}
	
	public ArrayList<Block> getBlocks()
	{
		return blocks;
	}
	
	public String simString(){
		String str = "Number of writes to journal area: " + writes + 
				"\nNumber of checkpoints due to journal space limit reached: " + checkptLimit;
		return str;
	}

}
